'''
This file is part of DopplerShift.

    DopplerShift is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DopplerShift is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DopplerShift.  If not, see <http://www.gnu.org/licenses/>.
'''
from dopplershift.collector.exc import Http404, Http500
from routes import Mapper
import time
from dopplershift.collector.handler import RequestHandler
import hashlib 
from twisted.python import log
import logging
from dopplershift.core.containers.mediator import getMediator
from dopplershift.collector.routing.route import Route
from dopplershift.collector.routing.condition import Condition
from dopplershift.collector.routing.call import CallInfo, ParameterInfo 
from dopplershift.collector.http import REQUEST_METHOD_NONE, REQUEST_METHOD_GET, REQUEST_METHOD_POST
logger = logging.getLogger(__name__)


class Router:
    def __init__(self):
        self._mapper = Mapper()
        self._routes = {}
        self._buildRoutes()
    def _buildRoutes(self):
        '''
        Build Routes mapper and connect urls from configuration file to routes map.
        '''
        self._i = 0
        for routes in getMediator().get('CollectorConfig').get_routes():
            for el in routes.xpath('route'):
                url = el.get('url')
                if url == None:
                    continue
                route = self._createRoute(el)
                controller = route.getControllerName()
                action = route.getActionName()
                self._routes["%s_%s"%(controller, action)] = route
                self._mapper.connect("route", url, controller=controller, action=action)
                if url[-1] != '/': self._mapper.connect("route", "%s/"%url, controller=controller, action=action)
    def _getNewIdent(self):
        self._i += 1
        return self._i
    def _createRoute(self, node):
        action = self._getNewIdent()
        controller = self._getNewIdent()
        route = Route(node.get("url"), controller, action)
        self._createConditions(node, route)
        return route
    def _createCall(self, node, condition):
        for el in node.xpath('call'):
            cfg = getMediator().get('CollectorConfig')
            service = cfg.get_service(el.get("id"))
            if service == None:
                continue
            
            caching_time = 0
            caching = False
            if service.get('caching') == 'true': 
                caching = True
                caching_time = int(service.get('cache_time'))
            params = []
            matching = []
            from lxml import etree
            for param in service.xpath('''./params/item'''):
                params.append(
                              ParameterInfo(
                                            param.get('key'),
                                            param.get('value'),
                                            param.get('default')
                                            )
                              )
            data_ctype = service.get('data_content_type', 'json')
            for param in service.xpath('''./matched_defaults/item'''):
                matching.append(
                              ParameterInfo(
                                            param.get('key'),
                                            param.get('key'),
                                            param.get('value')
                                            )
                              )
            call = CallInfo(
                        el.get('id'),
                        el.get('node'),
                        service.get('upstream'),
                        service.get('service'),
                        el.get('except'),
                        caching,
                        caching_time,
                        params=params,
                        matching=matching,
                        data_content_type=data_ctype
                        )
            condition.putCall(call)
    def _createConditions(self, node, route):
        for el in node.xpath('condition'):
            method = el.get("method")
            auth = False
            if el.get("auth") == 'true': auth = True
            
            if method == 'GET': method = REQUEST_METHOD_GET
            elif method == 'POST': method = REQUEST_METHOD_POST
            else:  method = REQUEST_METHOD_NONE
            
            content_type  = el.get("content_type")
            renderer = el.get('renderer')
            condition = Condition(el.get("template"), method, auth, content_type, renderer)
            
            route.putCondition(condition)
            self._createCall(el, condition)
    def getHandler(self, request):
        url =  self._getUrlFromRequest(request.postpath)
        logger.debug("{getHandler}, url: %s", url)
        request.current_url = url
        matched = self._mapper.match(url)
        if matched == None or 'action' not in matched or 'controller' not in matched:
            raise Http404(url)
        route = self._routes.get("%s_%s"%(matched['controller'], matched['action']))       
        if route == None:
            raise Http500(url)
        matched.pop('controller')
        matched.pop('action')
        return RequestHandler(request, matched, route) 
    
    def _getUrlFromRequest(self, req_url):
        return '/%s'%('/'.join(req_url))